package com.authine.cloudpivot.ext.service.voucher.impl;

import com.authine.cloudpivot.ext.Enum.InvoiceBusinessType;
import com.authine.cloudpivot.ext.Enum.InvoiceType;
import com.authine.cloudpivot.ext.Enum.SettleType;
import com.authine.cloudpivot.ext.constants.ModelConst;
import com.authine.cloudpivot.ext.constants.PlatformConst;
import com.authine.cloudpivot.ext.dto.kingDee.request.*;
import com.authine.cloudpivot.ext.dto.model.invoice.InvoiceApply;
import com.authine.cloudpivot.ext.dto.model.invoice.InvoiceDetailExtendsAttr;
import com.authine.cloudpivot.ext.dto.model.settle.Settle;
import com.authine.cloudpivot.ext.dto.model.settle.SettleDetail;
import com.authine.cloudpivot.ext.dto.model.settle.SettleInvoiceDetail;
import com.authine.cloudpivot.ext.enums.kingdee.BusinessType;
import com.authine.cloudpivot.ext.enums.kingdee.KingDeeInvoiceApplyTypeEnum;
import com.authine.cloudpivot.ext.enums.kingdee.KingDeePaymentApplyType;
import com.authine.cloudpivot.ext.service.InvoiceVoucherService;
import com.authine.cloudpivot.ext.service.MoneyVoucherService;
import com.authine.cloudpivot.ext.service.OtherVoucherService;
import com.authine.cloudpivot.ext.service.voucher.KingDeeFinanceService;
import com.authine.cloudpivot.ext.utils.JdbcTemplateUtils;
import com.authine.cloudpivot.ext.utils.StringUtils;
import com.authine.mvp.app.launcher.domain.SelectorFormat;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.ZoneId;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Author: liwei
 * @DateTime: 2021/8/16
 * @Description: TODO
 */
@Slf4j
public class KingDeeFinanceServiceImpl implements KingDeeFinanceService {

    public InvoiceVoucherService invoiceVoucherService = new InvoiceVoucherService();
    public OtherVoucherService otherVoucherService = new OtherVoucherService();
    public MoneyVoucherService moneyVoucherService = new MoneyVoucherService();

    @Override
    public void receiveInvoiceInvoke(InvoiceApply invoiceApply) {
        log.info("receiveInvoiceInvoke  request:{}", invoiceApply);

        String typeValue = invoiceApply.getBusiness_type();
        InvoiceBusinessType invoiceBusinessType = InvoiceBusinessType.valueOf(typeValue);

        BusinessType businessType;

        if (invoiceBusinessType.equals(InvoiceBusinessType.Service)) {
            businessType = BusinessType.PlatformVendorBlueTicket;

        } else if (invoiceBusinessType.equals(InvoiceBusinessType.Product)) {
            if (PlatformConst.PLATFORM_ORGANIZATION_IDS.contains(invoiceApply.getSale_organization_id())) {
                businessType = BusinessType.PlatformCustomerInvoice;
            } else {
                businessType = BusinessType.PlatformSupplierInvoice;
            }
        } else if (invoiceBusinessType.equals(InvoiceBusinessType.RedProduct)) {

            if (PlatformConst.PLATFORM_ORGANIZATION_IDS.contains(invoiceApply.getSale_organization_id())) {
                businessType = BusinessType.PlatformCustomerRedInvoice;
            } else {
                businessType = BusinessType.PlatformSupplierRedInvoice;
            }

        } else if (invoiceBusinessType.equals(InvoiceBusinessType.RedService)) {

            businessType = BusinessType.PlatformSupplierRedTicket;

        } else {
            log.error("确认开票调用KingDee失败：未找到对应的发票业务类型");
            return;
        }

        if (businessType == BusinessType.PlatformSupplierInvoice) {
            receiptAndSupplement(invoiceApply);
        } else if (businessType != BusinessType.PlatformSupplierRedInvoice) {

            SelectorFormat purchaseOrganizationId = invoiceApply.getPurchase_organization_id();
            SelectorFormat saleOrganizationId = invoiceApply.getSale_organization_id();
            String accountName = PlatformConst.getPlatformOrganizationName(purchaseOrganizationId.getId(), saleOrganizationId.getId());
            otherVoucherService.saveTemporaryInvoice(new TemporaryInvoiceDTO(invoiceApply.getId(), invoiceApply.getNumber(), businessType, accountName));
        }
        log.info("receiveInvoiceInvoke  End of  logic processing <<<");

    }

    @Override
    public void doInvalidInvoice(List<InvoiceApply> invoiceApplyList) {
        log.info("doInvalidInvoice  request:{}", invoiceApplyList);

        if (CollectionUtils.isEmpty(invoiceApplyList)) {
            log.error("发票申请单列表为空 !!!");
            return;
        }

        for (InvoiceApply invoiceApply : invoiceApplyList) {

            SelectorFormat saleOrganizationId = invoiceApply.getSale_organization_id();
            SelectorFormat purchaseOrganizationId = invoiceApply.getPurchase_organization_id();
            if (PlatformConst.PLATFORM_ORGANIZATION_IDS.contains(saleOrganizationId.getId())) {

                invoiceVoucherService.cancelInvoiceVoucher(invoiceApply.getId());
            } else {

                String accountName = PlatformConst.getPlatformOrganizationName(purchaseOrganizationId.getId(), saleOrganizationId.getId());
                invoiceVoucherService.cancelReceiveInvoiceVoucher(new KingDeeDeleteInvoice(invoiceApply.getId(), accountName));
            }

        }
        log.info("doInvalidInvoice  End of  logic processing <<<");

    }

    @Override
    public void settlePurchasePayment(List<Settle> settleList) {
        log.info("settlePurchasePayment  request:{}", settleList);

        for (Settle settle : settleList) {
            List<KingDeePlatFormPayments> kingDeePlatFormPaymentsList = new ArrayList<>();

            List<SettleDetail> settleDetailList = settle.getSettleDetailList();
            BigDecimal amount = BigDecimal.ZERO;
            for (SettleDetail settleDetail : settleDetailList) {
                amount = amount.add(settleDetail.getTotal());
            }
            kingDeePlatFormPaymentsList.add(KingDeePlatFormPayments.builder().id(settle.getId())
                    .vendorId(settle.getSale_organization_id().getId())
                    .vendorName(settle.getSale_organization_name())
                    .number(settle.getInvoice_number())
                    .financialServiceFee(settle.getInterest())
                    .platformServiceFee(settle.getPlatform())
                    .amountOfTheTransaction(amount)
                    .actualPaymentAmount(settle.getTotal()).build());

            KingDeePaymentApplyType kingDeePaymentApplyType = convertToKingDeeType(settle.getTypeEum());

            String accountName = PlatformConst.getPlatformOrganizationName(settle.getPurchase_organization_id().getId(), settle.getSale_organization_id().getId());
            KingDeePlatFormPayment kingDeePlatFormPayment = new KingDeePlatFormPayment();
            kingDeePlatFormPayment.setPays(kingDeePlatFormPaymentsList);
            kingDeePlatFormPayment.setBankNo(settle.getPurchase_bank_no());
            kingDeePlatFormPayment.setConfirmEndTime(settle.getPayment_time());
            kingDeePlatFormPayment.setPaymentApplyType(kingDeePaymentApplyType);
            kingDeePlatFormPayment.setAccountName(accountName);
            //调用金蝶凭证
            moneyVoucherService.platformPayment(kingDeePlatFormPayment);
        }
        log.info("settlePurchasePayment  End of  logic processing <<<");

    }

    @Override
    public void deleteAdvanceSettle(String id) {
        log.info("deleteAdvanceSettle  request:{}", id);

        Settle settle = JdbcTemplateUtils.builder(ModelConst.T_SETTLE).selectField("*")
                .eq("id", id).queryForPOJO(Settle.class);
        String accountName = PlatformConst.getPlatformOrganizationName(settle.getPurchase_organization_id().getId(), settle.getSale_organization_id().getId());

        KingDeeCancelPayment cancelPayment = new KingDeeCancelPayment(settle.getId(), accountName);
        moneyVoucherService.platformCancelPayment(Collections.singletonList(cancelPayment));
        log.info("deleteAdvanceSettle  End of  logic processing <<<");

    }

    @Override
    public void close(List<Settle> settleDoneList) {
        log.info("close  request:{}", settleDoneList);

        List<KingDeeCancelPayment> kingDeeCancelPaymentList = new ArrayList<>();
        for (Settle settle : settleDoneList) {
            String accountName = PlatformConst.getPlatformOrganizationName(settle.getPurchase_organization_id().getId(), settle.getSale_organization_id().getId());
            kingDeeCancelPaymentList.add(new KingDeeCancelPayment(settle.getId(), accountName));
        }
        moneyVoucherService.platformCancelPayment(kingDeeCancelPaymentList);
        log.info("close  End of  logic processing <<<");

    }

    @Override
    public void createAdvanceSettle(Settle settle) {
        log.info("createAdvanceSettle  request:{}", settle);

        String accountName = PlatformConst.getPlatformOrganizationName(settle.getPurchase_organization_id().getId(), settle.getSale_organization_id().getId());

        moneyVoucherService.platformCollection(KingDeePlatformCollection.builder().collectionAccount(settle.getSale_bank_no())
                                    .collectionTime(settle.getPayment_time())
                                    .paymentAmount(settle.getTotal())
                                    .paymentApplyType(convertToKingDeeType(settle.getTypeEum()))
                                    .paymentClient(settle.getPurchase_name())
                                    .receiptNumber(settle.getId())
                                    .contractPaymentType(settle.isContract()?settle.getContract_payment_type():null)
                                    .accountName(accountName).build());
        log.info("createAdvanceSettle  End of  logic processing <<<");

    }

    @Override
    public void refundConfirm(List<Settle> settleList) {
        log.info("refundConfirm  request:{}", settleList);

        for (Settle settle : settleList) {
            KingDeePaymentApplyType kingDeePaymentApplyType = convertToKingDeeType(settle.getTypeEum());
            String gmRise = PlatformConst.getPlatformOrganizationName(settle.getPurchase_organization_id().getId(), settle.getSale_organization_id().getId());
            KingDeePlatformCollection kingDeePlatformCollection = KingDeePlatformCollection.builder()
                    // 平台付款账号
                    .collectionAccount(settle.getPurchase_bank_no())
                    .collectionTime(settle.getConfirm_time())
                    .paymentAmount(settle.getTotal())
                    .paymentApplyType(kingDeePaymentApplyType)
                    // 供应商名称
                    .paymentClient(settle.getSale_organization_name())
                    // 供应商ID
                    .paymentClientId(settle.getSale_organization_id().getId())
                    .paymentPayable(settle.getSale_organization_name())
                    .receiptNumber(settle.getId())
                    // 组织抬头
                    .accountName(gmRise)
                    .build();
            moneyVoucherService.platformReturnCancelPayment(kingDeePlatformCollection);
        }
        log.info("refundConfirm  End of  logic processing <<<");

    }

    @Override
    public void refundCancel(List<Settle> settleList) {
        log.info("refundCancel  request:{}", settleList);
        if (CollectionUtils.isEmpty(settleList)) {
            log.info("refundCancel  request is null !!!");
            return;
        }
        List<String> settleIds = settleList.stream().map(Settle::getId).collect(Collectors.toList());
        List<Settle> settles = JdbcTemplateUtils.builder(ModelConst.T_SETTLE).selectField("*").in("id", settleIds).queryForPOJOList(Settle.class);
        for (Settle settle : settles) {
            String accountName = PlatformConst.getPlatformOrganizationName(settle.getPurchase_organization_id().getId(), settle.getSale_organization_id().getId());
            moneyVoucherService.platformCancelReturnCancelPayment(Collections.singletonList(new KingDeeCancelPayment(settleIds.get(0), accountName)));
        }
        log.info("refundCancel  End of  logic processing <<<");
    }

    @Override
    public void writeOffSettleByInvoice(Settle settleEntity) {
        log.info("writeOffSettleByInvoice  request:{}", settleEntity);

        List<SettleInvoiceDetail> settleInvoiceDetailList = settleEntity.getSettleInvoiceDetailList();
        //根据发票抬头分组后的数据
        List<KingDeePlatformWriteOffInvoiceDetail> writeOffInvoiceDetailList = new ArrayList<>();

        settleInvoiceDetailList.stream().collect(Collectors.groupingBy(SettleInvoiceDetail ::getInvoice_purchase_name))
                .forEach((invoicePurchaseNameKey,settleInvoiceDetailValue)->{
                    //按发票号码分组，获取发票号码
                    Set<String> numbers = new HashSet<>();
                    settleInvoiceDetailValue.stream().collect(Collectors.groupingBy(SettleInvoiceDetail::getNumber))
                            .forEach((invoiceNumberKey, settleInvoiceDetailInnerValue)->{
                                numbers.add(invoiceNumberKey);
                            });

                    BigDecimal amount = BigDecimal.ZERO;
                    for (SettleInvoiceDetail settleInvoiceDetail : settleInvoiceDetailValue) {
                        amount = amount.add(settleInvoiceDetail.getBatch_total());
                    }
                    writeOffInvoiceDetailList.add(KingDeePlatformWriteOffInvoiceDetail.builder()
                            .number(StringUtils.join(numbers,","))
                            .amount(amount)
                            .invoice(invoicePurchaseNameKey).build());


                });

        //如果发票抬头 和 收款抬头 一致 ，则不生成
        String purchaseName = settleEntity.getPurchase_name();
        if (null == purchaseName) {
            purchaseName = "";
        }else {
            String[] split = purchaseName.split(";");
            List<String> purchaseNameList = Arrays.asList(split);
            //获取结算单抬头最后一个抬头
            purchaseName = purchaseNameList.get(purchaseNameList.size()-1);
        }

        KingDeePlatformWriteOff kingDeePlatformWriteOff = null;

        List<KingDeePlatformWriteOffInvoiceDetail> detailList = new ArrayList<>();

        for (KingDeePlatformWriteOffInvoiceDetail kingDeePlatformWriteOffInvoiceDetail : writeOffInvoiceDetailList) {

            if (!(kingDeePlatformWriteOffInvoiceDetail.getInvoice().equals(purchaseName))) {

                String accountName = PlatformConst.getPlatformOrganizationName(settleEntity.getPurchase_organization_id().getId(), settleEntity.getSale_organization_id().getId());

                detailList.add(kingDeePlatformWriteOffInvoiceDetail);
                kingDeePlatformWriteOff = KingDeePlatformWriteOff.builder().receiptId(settleEntity.getId())
                        .receiptTime(settleEntity.getPayment_time())
                        .paymentPayable(purchaseName)
                        .writeOffAmount(settleEntity.getTotal())
                        .kingDeePlatformWriteOffInvoiceDetailList(detailList)
                        .accountName(accountName).build();

            }
        }

        if (null != kingDeePlatformWriteOff) {

            //核销真实金额 转换为 凭证真实金额 价格策略
            BigDecimal sum = BigDecimal.ZERO;
            for (KingDeePlatformWriteOffInvoiceDetail kingDeePlatformWriteOffInvoiceDetail : kingDeePlatformWriteOff.getKingDeePlatformWriteOffInvoiceDetailList()) {
                sum = sum.add(kingDeePlatformWriteOffInvoiceDetail.getAmount());
            }

            if (sum != kingDeePlatformWriteOff.getWriteOffAmount()){
                kingDeePlatformWriteOff.setWriteOffAmount(sum);
            }

            moneyVoucherService.platformWriteOff(kingDeePlatformWriteOff);
        }
        log.info("writeOffSettleByInvoice  End of  logic processing <<<");

    }

    @Override
    public void writeOffCancel(String id) {
        log.info("writeOffCancel  request:{}", id);

        Settle settle = JdbcTemplateUtils.builder(ModelConst.T_SETTLE).selectField("*")
                .eq("id", id).queryForPOJO(Settle.class);

        String accountName = PlatformConst.getPlatformOrganizationName(settle.getPurchase_organization_id().getId(), settle.getSale_organization_id().getId());
        moneyVoucherService.platformCancelWriteOff(Collections.singletonList(new KingDeeCancelPayment(settle.getId(),accountName)));
        log.info("writeOffCancel  End of  logic processing <<<");

    }


    /**
     * 结算单付款类型转换成金蝶付款类型
     *
     * @param type
     * @return
     */
    private KingDeePaymentApplyType convertToKingDeeType(SettleType type) {
        if (type.equals(SettleType.Balance)) {
            return KingDeePaymentApplyType.BALANCE;
        } else if (type.equals(SettleType.Money)) {
            return KingDeePaymentApplyType.MONEY;
        } else if (type.equals(SettleType.AliPay)) {
            return KingDeePaymentApplyType.ALI_PAY;
        } else if (type.equals(SettleType.Bank)) {
            return KingDeePaymentApplyType.BANK;
        } else if (type.equals(SettleType.PaperBillOfExchange)) {
            return KingDeePaymentApplyType.PAPER_BILL_OF_EXCHANGE;
        } else if (type.equals(SettleType.ElectronicBillOfExchange)) {
            return KingDeePaymentApplyType.ELECTRONIC_BILL_OF_EXCHANGE;
        } else {
            return KingDeePaymentApplyType.ALL;
        }
    }

    /**
     * @param invoiceApply
     */
    private void receiptAndSupplement(InvoiceApply invoiceApply) {
        //根据 发票申请id 获取发票明细列表
        List<Map<String, Object>> invoiceDetailList = JdbcTemplateUtils.builder(ModelConst.T_INVOICE_DETAIL).eq("invoice_apply_id", invoiceApply.getId())
                .selectField("*").queryForList();

        if (CollectionUtils.isEmpty(invoiceDetailList)) {
            log.error("平台收供应商发票 生成凭证时 获取发票明细列表为空 !!!");
            return;
        }
        Map<String, List<Map<String, Object>>> groupMap = invoiceDetailList.stream().collect(Collectors.groupingBy(e -> String.valueOf(e.get("trade_id"))));

        Set<String> tradeIdList = invoiceDetailList.stream().map(e -> String.valueOf(e.get("trade_id"))).collect(Collectors.toSet());
        //获取对应交易列表
        List<Map<String, Object>> tradeList = JdbcTemplateUtils.builder(ModelConst.T_TRADE).in("id", tradeIdList)
                .selectField("*").queryForList();

        if (CollectionUtils.isEmpty(tradeIdList)) {
            log.error("平台收供应商发票 生成凭证时 获取交易列表为空 !!!");
            return;
        }
        //补差金额
        BigDecimal suppleAmount = BigDecimal.ZERO;
        //获取预估金额 = 排除退款的交易总金额
        BigDecimal estimatedAmount = BigDecimal.ZERO;

        for (Map<String, Object> trade : tradeList) {
            BigDecimal price = new BigDecimal(String.valueOf(trade.get("price")));
            BigDecimal taxRate = new BigDecimal(String.valueOf(trade.get("tax_rate")));
            //未税单价
            BigDecimal prePrice = price.divide(taxRate.add(BigDecimal.ONE), 10, RoundingMode.HALF_UP);

            //获取交易对应开票信息
            List<Map<String, Object>> invoiceDetailTradeList = groupMap.get(String.valueOf(trade.get("id")));
            Assert.isTrue(!CollectionUtils.isEmpty(invoiceDetailTradeList) && invoiceDetailTradeList.size() == 1, "生成收票凭证时根据交易获取发票明细数据异常");

            Map<String, Object> invoiceDetail = invoiceDetailTradeList.get(0);

            BigDecimal quantity = new BigDecimal(String.valueOf(invoiceDetail.get("quantity")));
            BigDecimal offsetQuantity = new BigDecimal(String.valueOf(invoiceDetail.get("offset_quantity")));
            //参与计算有效发票数量=开票数量-红冲数量
            BigDecimal subQuantity = quantity.subtract(offsetQuantity);
            //交易的未税总计 保留2位数
            BigDecimal tradeAmount = prePrice.multiply(subQuantity).divide(BigDecimal.ONE, 2, RoundingMode.HALF_UP);
            //发票的未税总金额
            BigDecimal pretaxAmount = price.multiply(subQuantity).divide(BigDecimal.ONE, 2, RoundingMode.HALF_UP);

            estimatedAmount = estimatedAmount.add(tradeAmount);
            suppleAmount = suppleAmount.add(tradeAmount.subtract(pretaxAmount));

        }

        //t_invoice_record  根据开票申请单id 获取开票记录信息  invoice_apply_id
        List<Map<String, Object>> invoiceRecordList = JdbcTemplateUtils.builder(ModelConst.T_INVOICE_RECORD).in("invoice_apply_id", invoiceApply.getId())
                .selectField("*").queryForList();

        List<KingDeeInvoiceRecord> kingDeeInvoiceRecordList = new ArrayList<>();
        if (!CollectionUtils.isEmpty(invoiceRecordList)) {
            invoiceRecordList.stream().forEach(e -> {
                KingDeeInvoiceRecord kingDeeInvoiceRecord = KingDeeInvoiceRecord.builder().number(String.valueOf(e.get("number"))).taxPayable(new BigDecimal(String.valueOf(e.get("tax_payable")))).build();
                kingDeeInvoiceRecordList.add(kingDeeInvoiceRecord);
            });
        }

        String invoiceApplyType = invoiceApply.getType();
        KingDeeInvoiceApplyTypeEnum typeEnum;
        if (invoiceApplyType == InvoiceType.VatOrdinary.name()) {
            typeEnum = KingDeeInvoiceApplyTypeEnum.VAT_ORDINARY;
        } else if (invoiceApplyType == InvoiceType.VatSpecial.name()) {
            typeEnum = KingDeeInvoiceApplyTypeEnum.VAT_SPECIAL;
        } else {
            typeEnum = KingDeeInvoiceApplyTypeEnum.NO_INVOICING;
        }

        String saleId = invoiceApply.getSale_organization_id().getId();
        String purchaseId = invoiceApply.getPurchase_organization_id().getId();
        String accountName = PlatformConst.getPlatformOrganizationName(purchaseId, saleId);

        InvoiceDetailExtendsAttr invoiceDetailExtendsAttr = InvoiceDetailExtendsAttr.conversion(String.valueOf(invoiceDetailList.get(0).get("extends_attr"))).orElse(InvoiceDetailExtendsAttr.builder().build());
        String vendorOrderId = invoiceDetailExtendsAttr.getVendorOrderId();
        KingDeeReceiveInvoice kingDeeReceiveInvoice = new KingDeeReceiveInvoice();

        kingDeeReceiveInvoice.setId(invoiceApply.getId());
        kingDeeReceiveInvoice.setAmount(invoiceApply.getTotal());
        kingDeeReceiveInvoice.setConfirmOrganizationCompanyName(invoiceApply.getSale_organization_name());
        kingDeeReceiveInvoice.setConfirmOrganizationId(saleId);
        kingDeeReceiveInvoice.setConfirmTime(invoiceApply.getConfirm_time());
        kingDeeReceiveInvoice.setPretaxAmount(estimatedAmount);
        kingDeeReceiveInvoice.setPretaxAmounts(invoiceApply.getPrice_amount());
        kingDeeReceiveInvoice.setNumber(invoiceApply.getNumber());
        kingDeeReceiveInvoice.setTaxRate(new BigDecimal(String.valueOf(invoiceDetailList.get(0).get("tax_rate"))));
        kingDeeReceiveInvoice.setInvoiceRecordList(kingDeeInvoiceRecordList);
        kingDeeReceiveInvoice.setType(typeEnum);
        kingDeeReceiveInvoice.setVendorOrderId(null == vendorOrderId ? "" : vendorOrderId);
        kingDeeReceiveInvoice.setSuppleAmount(suppleAmount);
        kingDeeReceiveInvoice.setAttachments(invoiceApply.getNumber_attachments());
        kingDeeReceiveInvoice.setAccountName(accountName);

        invoiceVoucherService.receiveInvoiceVoucher(kingDeeReceiveInvoice);
    }
}
